//
//  JXNetwork.swift
//  Demo
//
//  Created by xiu on 2020/4/20.
//  Copyright © 2020 111. All rights reserved.
//

import UIKit
import Alamofire
import YJEncry

public enum JXContentType {
    case json
    case data
}

public enum JXUrlEncryptType:String {
    case aes = "aes"
    case xxtea = "xxtea"
    case none = "none"
}

public protocol JXRequestProtocol {
    var host: String {get}
    var path: String {get}
    var params: Dictionary<String, Any>? {get}
    var method: HTTPMethod {get}
    var encrypt: JXUrlEncryptType {get}
    var contentType: JXContentType {get}
    var headers: Dictionary<String, String>? {get}
    var headerFiles: Dictionary<String, String>? {get}
    func response(completionHandler: @escaping CompletionHandler)
}

public extension JXRequestProtocol {
    var host: String {
        return ""
    }
    var path: String {
        return ""
    }
    var params: Dictionary<String, Any>? {
        return nil
    }
    var method: HTTPMethod {
        return .get
    }
    var encrypt: JXUrlEncryptType {
        return .none
    }
    var contentType: JXContentType {
        return .json
    }
    var headers: Dictionary<String, String>? {
        return nil
    }
    var headerFiles: Dictionary<String, String>? {
        return nil
    }
    func response(completionHandler: @escaping CompletionHandler) {
        JXNetwork.request(self, completionHandler)
    }
}

public typealias CompletionHandler = (_ response: JXResponse) -> Void

public class JXNetwork {
    
    public static var apiHost: String = "http://potato.gtxxz.com:8088/potato/json"
    public static var apiKey: String = "52_kevin_babay_forever"
    public static var xxteaKey: String = "$ZNgRnXb7#u7PDS#"
    public static var appPackage: String = ""
    public static var appVer: String = ""
    public static var appId: String = ""
    
    public static var udid: String = ""
    public static var idfa: String = ""
    public static var deviceParams: Dictionary<String, Any>?
    
    static let AlamofireManger: Alamofire.Session = {
        let SessionManager = Alamofire.Session.default
        SessionManager.sessionConfiguration.timeoutIntervalForRequest = 15
        return SessionManager
    }()
    
}

public extension JXNetwork {

    static func request<T:JXRequestProtocol>(_ args:T, _ completionHandler: @escaping CompletionHandler) {
        DispatchQueue.init(label: "JXNetwork").async {
            guard let urlRequest = makeURLRequest(args) else {
                let response = JXResponse.requestError()
                completionHandler(response)
                return
            }
            
            let logs = [
                "请求网络",
                "host: \(args.host)",
                "path: \(args.path)",
                "params: \(String(describing: args.params))",
                "method: \(args.method)",
                "encrypt: \(args.encrypt)",
                "contentType: \(args.contentType)",
                "headers: \(String(describing: args.headers))",
                "headerFiles: \(String(describing: args.headerFiles))",
                "fullURL: \(urlRequest.url?.absoluteString ?? "")"
            ]
            
            print(logs)
            
            let dataRequest = JXNetwork.AlamofireManger.request(urlRequest)
            
            switch args.contentType {
            case .data:
                dataRequest.responseData { (r) in
                    DispatchQueue.main.async {
                        let response = JXResponse.init(response: r)
                        completionHandler(response)
                    }
                }
                break
            case .json:
                dataRequest.responseJSON { (r) in
                    DispatchQueue.main.async {
                        let response = JXResponse.init(response: r)
                        completionHandler(response)
                    }
                }
                break
            }
        }
    }
    
    
    private static func makeURLRequest<T:JXRequestProtocol>(_ args:T) -> URLRequest? {
        
        var newParams: [String: Any] = args.params ?? [:]
        
        var requestURL = args.host + args.path
        
        requestURL = getFullUrlPath(requestURL)
        
        guard var originalRequest = try? URLRequest.init(url: requestURL, method: args.method, headers: HTTPHeaders(args.headers ?? ["":""]) ) else {
            return nil
        }
        
        do {
            if let headerFiles = args.headerFiles {
                for key in headerFiles.keys {
                    originalRequest.addValue(headerFiles[key]!, forHTTPHeaderField:key)
                }
            }
            
            if args.method == .post {
                switch args.encrypt {
                case .aes:
                    if let device = deviceParams {
                        newParams.updateValue(device, forKey: "device")
                    }
                    originalRequest.httpBody = aesData(object: newParams)
                    break
                case .xxtea:
                    originalRequest.httpBody = xxteaData(object: newParams)
                    break
                default:
                    originalRequest.httpBody = normalData(object: newParams)
                    break
                }
                return originalRequest
            }
            else {
                originalRequest = try URLEncoding.default.encode(originalRequest, with: newParams)
                return originalRequest
            }
        } catch {return nil}
        
    }
    
    private static func currenttime() -> String {
        return "\(Int(Date().timeIntervalSince1970))"
    }
    
    private static func getFullUrlPath(_ path:String) -> String {
        let currenttime = JXNetwork.currenttime()
        
        var publicParams: [String: String] = [:]
        publicParams.updateValue(JXNetwork.appVer, forKey: "ver")
        publicParams.updateValue(JXNetwork.appPackage, forKey: "bundleId")
        publicParams.updateValue("ios", forKey: "osType")
        publicParams.updateValue("IOS", forKey: "platform")
        publicParams.updateValue(JXNetwork.appId, forKey: "app_id")
        publicParams.updateValue(currenttime, forKey: "ts")
        publicParams.updateValue(JXNetwork.udid, forKey: "uuid")
        publicParams.updateValue(TokenGenerator.tokenGeneration(with: JXNetwork.appVer, uuid: JXNetwork.udid, appid: JXNetwork.appId, platform: "IOS", appkey: JXNetwork.apiKey, ts: currenttime), forKey: "token")
        
        var fullPath: String = path
        fullPath.append("?")
        
        for key in publicParams.keys.sorted(by: <) {
            let value = publicParams[key]!
            fullPath.append(key+"="+value+"&")
        }
        if fullPath.hasSuffix("&") {
            let toLen = fullPath.count-1
            fullPath = String(fullPath.prefix(toLen))
        }
        return fullPath
    }
    
    private static func aesData(object: [String : Any]?, msTime: String = currenttime()) -> Data? {
        guard let param = object else {
            return nil
        }
        guard JSONSerialization.isValidJSONObject(param), let data = try? JSONSerialization.data(withJSONObject: param, options: []) else {
            return nil
        }
        guard let jsonString = String(data: data, encoding: .utf8) else { return nil }
        return TokenGenerator.neu_encryptAESData2(jsonString).data(using: .utf8)
    }
    
    private static func xxteaData(object: [String : Any]?, msTime: String = currenttime()) -> Data? {
        guard let param = object else {
            return nil
        }
        guard JSONSerialization.isValidJSONObject(param), let data = try? JSONSerialization.data(withJSONObject: param, options: []) else {
            return nil
        }
        guard let jsonString = String(data: data, encoding: .utf8) else { return nil }
        return YJXXTEA.encryptString(toBase64String: jsonString, stringKey: xxteaKey).data(using: .utf8)
    }
    
    private static func normalData(object: [String : Any]?, msTime: String = currenttime()) -> Data? {
        guard let param = object else {
            return nil
        }
        guard JSONSerialization.isValidJSONObject(param), let data = try? JSONSerialization.data(withJSONObject: param, options: []) else {
            return nil
        }
        return String(data: data, encoding: .utf8)?.data(using: .utf8)
    }
}

